#include <iostream>
#include <vector>
#include <map>
#include <sstream>
#include <fstream>

#include <sys/timeb.h>
#include <time.h>

#include "CThread.h"
#include "CBlockStream.h"
//#include "CBufferedStream.h"

#define MAX_LINE_LIMIT 10000

using namespace std;

class CRandomLineTokenizerThread : public CThread
{
private:
	CBlockStream *block_stream;

	map<string,int> keys;

	int kpos;
	vector<int> ppos;
	char delim;
	char eol;
	long limit;
	long num;

public:
	CRandomLineTokenizerThread() {
	}
	virtual ~CRandomLineTokenizerThread() {
		delete block_stream;
	}

	void run() {
		block_stream->resize(50);
		random_line_print(ppos, kpos, delim, eol, limit, num);
	}	

	void assign(long block, long blocks) {
		block_stream->assignBlock(block, blocks);
	}

	void set(char *file) {
		block_stream = new CBlockStream(file);

		kpos = 1;
		delim = '\t';
		eol = '\n';
		limit = MAX_LINE_LIMIT;
		num = 100;
	}

	void set(vector<int> &ppos, int kpos, char delim, char eol, long limit, long num) {
		for(int i=0;i<ppos.size();i++) this->ppos.push_back(ppos[i]);

		this->kpos = kpos;
		this->delim = delim;
		this->eol = eol;
		this->limit = limit;
		this->num = num;
	}
	
	map<string, int> &get() {
		return keys;
	}

	void randomize(int seed)
	{
		seed += getpid();

		srand(seed);
	}

	long just_random()
	{
		return rand();
	}

	long time_random()
	{
		struct timeb itb;
		struct tm *lt;
		ftime(&itb);
		lt = localtime(&itb.time);
		srand(lt->tm_min+lt->tm_sec+itb.millitm);

		return rand();
	}

	vector<long> &sorted_random(vector<long> &rpos, long count, long size)
	{
		for(int i=0;i<count;i++) rpos.push_back(just_random()%size);
		sort(rpos.begin(),rpos.end());

		return rpos;
	}

	long random_line_seek(CBlockStream *block_stream)
	{
		long rpos = just_random() % block_stream->size();

		return block_stream->adjust_seekg(rpos);
	}

	void random_line_print(vector<int> &ppos, int kpos, char delim, char eol, long limit, long rnum)
	{
		char uniq_file[100];
        string thread_id = getName();
        sprintf(uniq_file, "xxx-%d-%s", getpid(), (char *)thread_id.c_str());

#ifdef DEBUG
        ofstream fout(uniq_file, ios::app|ios::out);
#endif

		randomize(time_random());

		int key = kpos-1;
		vector< vector<string> > kv_vectors;

#ifdef NOT_SORTED_RANDOM
		for(int r=0;r<rnum;r++) {
			// random position
			block_stream->clear();
			long pos = random_line_seek(block_stream);

			if(block_stream->getline(kv_vectors, delim, limit)>0) {
				for(int i=0;i<kv_vectors.size();i++) {
#ifdef DEBUG
					fout << "#[" << kv_vectors[i][key] << "]" << endl;
#endif

					keys[kv_vectors[i][key]]++;

#ifdef DEBUG
					for(int j=0;j<ppos.size();j++) {
						fout << kv_vectors[i][ppos[j]-1] << delim;
					}
					fout << endl;
#endif
				}

				kv_vectors.clear();
			}
		}
#else
		vector<long> rpos;
		sorted_random(rpos, rnum, block_stream->size());

		for(int i=0;i<rpos.size();i++) {
			block_stream->adjust_seekg(rpos[i]);

			if(block_stream->getline(kv_vectors, delim, limit)>0) {
				for(int i=0;i<kv_vectors.size();i++) {
#ifdef DEBUG
					fout << "#[" << kv_vectors[i][key] << "]" << endl;
#endif

					keys[kv_vectors[i][key]]++;

#ifdef DEBUG
					for(int j=0;j<ppos.size();j++) {
						fout << kv_vectors[i][ppos[j]-1] << delim;
					}
					fout << endl;
#endif
				}

				kv_vectors.clear();
			}
		}
#endif
	}

};



void randomize(int seed)
{
	seed += getpid();

	srand(seed);
}

long just_random()
{
	return rand();
}

long time_random()
{
	struct timeb itb;
	struct tm *lt;
	ftime(&itb);
	lt = localtime(&itb.time);
	srand(lt->tm_min+lt->tm_sec+itb.millitm);

	return rand();
}

vector<long> &sorted_random(vector<long> &rpos, long count, long size)
{
	for(int i=0;i<count;i++) rpos.push_back(just_random()%size);
	sort(rpos.begin(),rpos.end());

	return rpos;
}

long random_line_seek(CBlockStream *block_stream)
{
	long rpos = just_random() % block_stream->size();

	return block_stream->adjust_seekg(rpos);
}

void random_line_print(CBlockStream *block_stream, vector<int> &ppos, int kpos, char delim, char eol, long limit, long rnum)
{
	randomize(time_random());

	int key = kpos-1;
	vector< vector<string> > kv_vectors;

#ifndef REAL_RANDOM
	for(int r=0;r<rnum;r++) {
		// random position
		block_stream->clear();
		long pos = random_line_seek(block_stream);

		if(block_stream->getline(kv_vectors, delim, limit)>0) {
			for(int i=0;i<kv_vectors.size();i++) {
				cout << "#[" << kv_vectors[i][key] << "]" << endl;

				for(int j=0;j<ppos.size();j++) {
					cout << kv_vectors[i][ppos[j]-1] << delim;
				}
				cout << endl;
			}

			kv_vectors.clear();
		}
	}
#else
	vector<long> rpos;
	sorted_random(rpos, rnum, block_stream->size());

	for(int i=0;i<rpos.size();i++) {
		block_stream->adjust_seekg(rpos[i]);

		if(block_stream->getline(kv_vectors, delim, limit)>0) {
			for(int i=0;i<kv_vectors.size();i++) {
				cout << "#[" << kv_vectors[i][key] << "]" << endl;

				for(int j=0;j<ppos.size();j++) {
					cout << kv_vectors[i][ppos[j]-1] << delim;
				}
				cout << endl;
			}

			kv_vectors.clear();
		}
	}
#endif
}

int main(int argc,char *argv[])
{
	if(argc<2) {
		cout << "Usages : " << argv[0] << " -f <file> -k <pos> -d <delim> -e <eol> -p <pos to print> -b <block> -t <blocks> -n <random line number>" << endl;
		exit(-1);
	}

	int opt;
	static char optstr[] = "fkpdebtn";

	char *file = NULL;
	int kpos = 1;
	vector<int> ppos;
	char delim = '\t';
	char eol = '\n';
	//long limit = MAX_LINE_LIMIT;
	long limit = 3;
	long block = 0;
	long blocks = 1;
	long num = 100;

	while((opt=getopt(argc,argv,optstr))!=-1) {
		switch(opt) {
			case 'f' :
					file = argv[optind++];
					break;
			case 'k' :
					kpos = atoi(argv[optind++]);
					break;
			case 'p' :
					ppos.push_back(atoi(argv[optind++]));
					break;
			case 'd' :
					delim = (char) *(argv[optind++]);
					break;
			case 'e' :
					eol = (char) *(argv[optind++]);
					break;
			case 'b' :
					block = atoi(argv[optind++]);
					break;
			case 't' :
					blocks = atoi(argv[optind++]);
					break;
			case 'n' :
					num = atoi(argv[optind++]);
					break;
		}
	}

	if(file!=NULL) {
#ifdef ONE_THREAD
		CBlockStream block_stream(file);

		block_stream.resize(50);
		random_line_print(&block_stream, ppos, kpos, delim, eol, limit, num/limit);
#else
		vector< CRandomLineTokenizerThread *> tok_threads;

		for(int i=0;i<blocks;i++) {
			CRandomLineTokenizerThread *thr = new CRandomLineTokenizerThread();

			tok_threads.push_back(thr);

			tok_threads[i]->set(file);
			tok_threads[i]->set(ppos, kpos, delim, eol, limit, (num/blocks)/limit);
			tok_threads[i]->assign(i, blocks);
			tok_threads[i]->start();
		}

		map<string,int> all_keys;
		for(int i=0;i<blocks;i++) {
			tok_threads[i]->join();

			map<string,int> keys = tok_threads[i]->get();
			map<string,int>::iterator key = keys.begin();
			while(key!=keys.end()) {
				all_keys[key->first] += key->second;

				key++;
			}

			delete tok_threads[i];
		}

		map<string,int>::iterator thekey = all_keys.begin();
		while(thekey!=all_keys.end()) {
			cout << "[" << thekey->first << "] = " << thekey->second << endl;

			thekey++;
		}
#endif
	}
}
